#include <iostream>
#include <sys/wait.h>
#include <unistd.h>
#include <assert.h>
#include <vector>
#include <string>
#include <cstring>
#include <time.h>
#include "command.hpp"

using namespace std;

#define PROGRESS_NUM 5 // 将会创建五个子进程

void getCommand(int pipefd)
{
    int recept = -1;
    int ret_read = read(pipefd, &recept, sizeof(recept));
    if (recept != -1)
        cout << "[" << getpid() << "]"
             << "i get command : " << desc_task[recept] << endl;
    if (ret_read == sizeof(recept)) // 就是正好传过来了一个数
    {
        if (ret_read >= 0 && ret_read < task_tb.size())
        {
            // 执行命令
            task_tb[recept]();
            cout << endl;
        }
        else
        {
            cout << "接收到的命令非法" << endl;
        }
    }
    else if (ret_read == 0) // 代表父进程的写管道被关闭了那我们子进程也可以退出了
    {
        cout << "[" << getpid() << "]: quit" << endl;
        // 关闭读管道并退出子进程
        close(pipefd);
        exit(0);
    }
}

int main()
{
    load();                      // 加载任务
    vector<pair<int, int>> p_tb; //<(pid,pipefd)键值对>父进程用来管理管道和相对应的子进程的进程表
    for (int i = 0; i < PROGRESS_NUM; i++)
    {
        // 创建管道
        int pipefd[2] = {0};
        int pipe_ret = pipe(pipefd);
        assert(pipe_ret != -1);

        // 创建子进程
        pid_t pid = fork();
        assert(pid >= 0);
        if (pid == 0) // 子进程
        {
            // 关闭写管道
            close(pipefd[1]);

            while (true)
            {
                // 处理接收到的父进程的命令
                getCommand(pipefd[0]);
            }
        }
        // 关闭父进程读管道
        close(pipefd[0]);

        // 将管道和创建的子进程联系起来输入到P_tb中
        p_tb.push_back(make_pair(pid, pipefd[1]));
    }
    // 父进程开始发送命令
    srand((unsigned int)time(nullptr)); // 种下随机数种子

    int count = 0;

    while (true)
    {
        int task = rand() % task_tb.size();
        int n = rand() % PROGRESS_NUM;
        cout << "I am parent send command: " << desc_task[task] << " to [" << p_tb[n].first << "]" << endl;
        write(p_tb[n].second, &task, sizeof(task)); // 发送命令
        sleep(1);                                   // 1秒发一次
        count++;
        if (count == 10)
            break;
    }

    for (const auto &it : p_tb) // 关闭父进程对应的每一个写管道使得子进程可以退出
    {
        close(it.second); // 关闭管道
    }

    for (const auto &it : p_tb) // 等待子进程
    {
        int wait_ret = waitpid(it.first, nullptr, 0); // 等待子进程退出
        assert(wait_ret != -1);
    }

    return 0;
}